Micron Document
🎖️GitЯра🎖️

Commit 9c8532f80d33a3b24fab5bb7d74ea3ad9b14305f


Parents : 929e273
Author : James Rich <2199651+jamesarich@users.noreply.github.com>
Signature : Signature validation error
Date : 2026-04-10T17:51:08-05:00
Committer : GitHub <noreply@github.com>
Date : 2026-04-10T22:51:08Z

refactor: leverage new dependency features from recent updates (#5057)

Changes
Diff

diff --git a/app/src/google/kotlin/org/meshtastic/app/map/MapView.kt b/app/src/google/kotlin/org/meshtastic/app/map/MapView.kt
index 530fc0c7b1..0418d76b79 100644
--- a/app/src/google/kotlin/org/meshtastic/app/map/MapView.kt
+++ b/app/src/google/kotlin/org/meshtastic/app/map/MapView.kt
@@ -21,8 +21,6 @@ package org.meshtastic.app.map
import android.Manifest
import android.app.Activity
import android.content.Intent
-import android.graphics.Canvas
-import android.graphics.Paint
import android.net.Uri
import android.view.WindowManager
import androidx.activity.compose.rememberLauncherForActivityResult
@@ -56,7 +54,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
-import androidx.core.graphics.createBitmap
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.touchlab.kermit.Logger
import com.google.accompanist.permissions.ExperimentalPermissionsApi
@@ -67,8 +64,6 @@ import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.Priority
import com.google.android.gms.maps.CameraUpdateFactory
-import com.google.android.gms.maps.model.BitmapDescriptor
-import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.JointType
import com.google.android.gms.maps.model.LatLng
@@ -798,7 +793,6 @@ private fun MainMapContent(
mapFilterState = mapFilterState,
myNodeNum = myNodeNum ?: 0,
isConnected = isConnected,
- unicodeEmojiToBitmapProvider = ::unicodeEmojiToBitmap,
onEditWaypointRequest = onEditWaypointRequest,
selectedWaypointId = selectedWaypointId,
)
@@ -1068,23 +1062,6 @@ internal fun convertIntToEmoji(unicodeCodePoint: Int): String = try {
"\uD83D\uDCCD"
}
-internal fun unicodeEmojiToBitmap(icon: Int): BitmapDescriptor {
- val unicodeEmoji = convertIntToEmoji(icon)
- val paint =
- Paint(Paint.ANTI_ALIAS_FLAG).apply {
- textSize = 64f
- color = android.graphics.Color.BLACK
- textAlign = Paint.Align.CENTER
- }
- val baseline = -paint.ascent()
- val width = (paint.measureText(unicodeEmoji) + 0.5f).toInt()
- val height = (baseline + paint.descent() + 0.5f).toInt()
- val image = createBitmap(width, height, android.graphics.Bitmap.Config.ARGB_8888)
- val canvas = Canvas(image)
- canvas.drawText(unicodeEmoji, width / 2f, baseline, paint)
- return BitmapDescriptorFactory.fromBitmap(image)
-}
-
@Suppress("NestedBlockDepth")
fun Uri.getFileName(context: android.content.Context): String {
var name = this.lastPathSegment ?: "layer_$nowMillis"

diff --git a/app/src/google/kotlin/org/meshtastic/app/map/component/WaypointMarkers.kt b/app/src/google/kotlin/org/meshtastic/app/map/component/WaypointMarkers.kt
index d4a53dcc43..61cdab9f14 100644
--- a/app/src/google/kotlin/org/meshtastic/app/map/component/WaypointMarkers.kt
+++ b/app/src/google/kotlin/org/meshtastic/app/map/component/WaypointMarkers.kt
@@ -16,15 +16,22 @@
*/
package org.meshtastic.app.map.component
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
-import com.google.android.gms.maps.model.BitmapDescriptor
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
import com.google.android.gms.maps.model.LatLng
+import com.google.maps.android.compose.MapsComposeExperimentalApi
import com.google.maps.android.compose.Marker
+import com.google.maps.android.compose.rememberComposeBitmapDescriptor
import com.google.maps.android.compose.rememberUpdatedMarkerState
import kotlinx.coroutines.launch
+import org.meshtastic.app.map.convertIntToEmoji
import org.meshtastic.core.model.util.GeoConstants.DEG_D
import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.locked
@@ -32,13 +39,13 @@ import org.meshtastic.core.ui.util.showToast
import org.meshtastic.feature.map.BaseMapViewModel
import org.meshtastic.proto.Waypoint
+@OptIn(MapsComposeExperimentalApi::class)
@Composable
fun WaypointMarkers(
displayableWaypoints: List<Waypoint>,
mapFilterState: BaseMapViewModel.MapFilterState,
myNodeNum: Int,
isConnected: Boolean,
- unicodeEmojiToBitmapProvider: (Int) -> BitmapDescriptor,
onEditWaypointRequest: (Waypoint) -> Unit,
selectedWaypointId: Int? = null,
) {
@@ -57,14 +64,16 @@ fun WaypointMarkers(
}
}
+ val iconCodePoint = if ((waypoint.icon ?: 0) == 0) PUSHPIN else waypoint.icon!!
+ val emojiText = convertIntToEmoji(iconCodePoint)
+ val icon =
+ rememberComposeBitmapDescriptor(iconCodePoint) {
+ Text(text = emojiText, fontSize = 32.sp, modifier = Modifier.padding(2.dp))
+ }
+
Marker(
state = markerState,
- icon =
- if ((waypoint.icon ?: 0) == 0) {
- unicodeEmojiToBitmapProvider(PUSHPIN) // Default icon (Round Pushpin)
- } else {
- unicodeEmojiToBitmapProvider(waypoint.icon!!)
- },
+ icon = icon,
title = (waypoint.name ?: "").replace('\n', ' ').replace('\b', ' '),
snippet = (waypoint.description ?: "").replace('\n', ' ').replace('\b', ' '),
visible = true,

diff --git a/app/src/test/kotlin/org/meshtastic/app/ui/NavigationAssemblyTest.kt b/app/src/test/kotlin/org/meshtastic/app/ui/NavigationAssemblyTest.kt
index ef4dab3e69..0665d50dbe 100644
--- a/app/src/test/kotlin/org/meshtastic/app/ui/NavigationAssemblyTest.kt
+++ b/app/src/test/kotlin/org/meshtastic/app/ui/NavigationAssemblyTest.kt
@@ -16,7 +16,7 @@
*/
package org.meshtastic.app.ui
-import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.rememberNavBackStack

diff --git a/core/barcode/src/test/kotlin/org/meshtastic/core/barcode/BarcodeScannerTest.kt b/core/barcode/src/test/kotlin/org/meshtastic/core/barcode/BarcodeScannerTest.kt
index bd34905663..e06562cfbf 100644
--- a/core/barcode/src/test/kotlin/org/meshtastic/core/barcode/BarcodeScannerTest.kt
+++ b/core/barcode/src/test/kotlin/org/meshtastic/core/barcode/BarcodeScannerTest.kt
@@ -16,7 +16,7 @@
*/
package org.meshtastic.core.barcode
-import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.junit4.v2.createComposeRule
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts
index f3e86f0c9f..08ec08865a 100644
--- a/core/common/build.gradle.kts
+++ b/core/common/build.gradle.kts
@@ -41,8 +41,6 @@ kotlin {
}
androidMain.dependencies { api(libs.androidx.core.ktx) }
- val androidHostTest by getting { dependencies { implementation(libs.robolectric) } }
-
commonTest.dependencies { implementation(libs.kotlinx.coroutines.test) }
}
}

diff --git a/core/database/src/commonMain/kotlin/org/meshtastic/core/database/Converters.kt b/core/database/src/commonMain/kotlin/org/meshtastic/core/database/Converters.kt
index 35746f68f5..67433459c8 100644
--- a/core/database/src/commonMain/kotlin/org/meshtastic/core/database/Converters.kt
+++ b/core/database/src/commonMain/kotlin/org/meshtastic/core/database/Converters.kt
@@ -18,6 +18,7 @@ package org.meshtastic.core.database
import androidx.room3.TypeConverter
import co.touchlab.kermit.Logger
+import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import okio.ByteString
import okio.ByteString.Companion.toByteString
@@ -33,10 +34,12 @@ import org.meshtastic.proto.User
@Suppress("TooManyFunctions")
class Converters {
+ @OptIn(ExperimentalSerializationApi::class)
private val json = Json {
isLenient = true
ignoreUnknownKeys = true
encodeDefaults = true
+ exceptionsWithDebugInfo = false
}
@TypeConverter fun dataFromString(value: String): DataPacket = json.decodeFromString(DataPacket.serializer(), value)

diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts
index 8a5f3fb216..1c0d14a01b 100644
--- a/core/network/build.gradle.kts
+++ b/core/network/build.gradle.kts
@@ -63,9 +63,6 @@ kotlin {
commonTest.dependencies {
implementation(projects.core.testing)
implementation(libs.kotlinx.coroutines.test)
- implementation(libs.turbine)
- implementation(libs.kotest.assertions)
- implementation(libs.kotest.property)
}
}
}

diff --git a/core/network/src/commonMain/kotlin/org/meshtastic/core/network/di/CoreNetworkModule.kt b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/di/CoreNetworkModule.kt
index 9b9d498288..0fbed14a85 100644
--- a/core/network/src/commonMain/kotlin/org/meshtastic/core/network/di/CoreNetworkModule.kt
+++ b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/di/CoreNetworkModule.kt
@@ -16,6 +16,7 @@
*/
package org.meshtastic.core.network.di
+import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import org.koin.core.annotation.ComponentScan
import org.koin.core.annotation.Module
@@ -24,10 +25,12 @@ import org.koin.core.annotation.Single
@Module
@ComponentScan("org.meshtastic.core.network")
class CoreNetworkModule {
+ @OptIn(ExperimentalSerializationApi::class)
@Single
fun provideJson(): Json = Json {
isLenient = true
ignoreUnknownKeys = true
coerceInputValues = true
+ exceptionsWithDebugInfo = false
}
}

diff --git a/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt
index 4b95f0191d..41fb652ed2 100644
--- a/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt
+++ b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt
@@ -35,6 +35,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
+import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import okio.ByteString.Companion.toByteString
import org.koin.core.annotation.Single
@@ -62,7 +63,12 @@ class MQTTRepositoryImpl(
}
private var client: MQTTClient? = null
- private val json = Json { ignoreUnknownKeys = true }
+
+ @OptIn(ExperimentalSerializationApi::class)
+ private val json = Json {
+ ignoreUnknownKeys = true
+ exceptionsWithDebugInfo = false
+ }
private val scope = CoroutineScope(dispatchers.default + SupervisorJob())
private var clientJob: Job? = null
private val publishSemaphore = Semaphore(20)
@@ -115,6 +121,9 @@ class MQTTRepositoryImpl(
Logger.d { "MQTT parsed JSON payload successfully" }
trySend(MqttClientProxyMessage(topic = topic, text = jsonStr, retained = packet.retain))
+ } catch (e: kotlinx.serialization.json.JsonDecodingException) {
+ @OptIn(ExperimentalSerializationApi::class)
+ Logger.e(e) { "Failed to parse MQTT JSON: ${e.shortMessage} (path: ${e.path})" }
} catch (e: kotlinx.serialization.SerializationException) {
Logger.e(e) { "Failed to parse MQTT JSON: ${e.message}" }
} catch (e: IllegalArgumentException) {

diff --git a/core/takserver/build.gradle.kts b/core/takserver/build.gradle.kts
index a1b1a7acba..02343cae34 100644
--- a/core/takserver/build.gradle.kts
+++ b/core/takserver/build.gradle.kts
@@ -56,9 +56,6 @@ kotlin {
commonTest.dependencies {
implementation(projects.core.testing)
implementation(libs.kotlinx.coroutines.test)
- implementation(libs.turbine)
- implementation(libs.kotest.assertions)
- implementation(libs.kotest.property)
}
}
}

diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts
index 05d276cf0c..bbe3204e53 100644
--- a/core/ui/build.gradle.kts
+++ b/core/ui/build.gradle.kts
@@ -70,9 +70,6 @@ kotlin {
implementation(projects.core.testing)
implementation(libs.junit)
implementation(libs.kotlinx.coroutines.test)
- implementation(libs.turbine)
- implementation(libs.kotest.assertions)
- implementation(libs.kotest.property)
}
val androidHostTest by getting { dependencies { implementation(libs.androidx.test.runner) } }

diff --git a/core/ui/src/androidTest/kotlin/org/meshtastic/core/ui/component/AlertHostTest.kt b/core/ui/src/androidTest/kotlin/org/meshtastic/core/ui/component/AlertHostTest.kt
index 5afc97a6f8..b6abd64b04 100644
--- a/core/ui/src/androidTest/kotlin/org/meshtastic/core/ui/component/AlertHostTest.kt
+++ b/core/ui/src/androidTest/kotlin/org/meshtastic/core/ui/component/AlertHostTest.kt
@@ -17,7 +17,7 @@
package org.meshtastic.core.ui.component
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import org.junit.Rule
import org.junit.Test

diff --git a/core/ui/src/androidTest/kotlin/org/meshtastic/core/ui/component/ImportFabUiTest.kt b/core/ui/src/androidTest/kotlin/org/meshtastic/core/ui/component/ImportFabUiTest.kt
index 460a96bc7f..cc4f32b8e7 100644
--- a/core/ui/src/androidTest/kotlin/org/meshtastic/core/ui/component/ImportFabUiTest.kt
+++ b/core/ui/src/androidTest/kotlin/org/meshtastic/core/ui/component/ImportFabUiTest.kt
@@ -20,7 +20,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.test.assertDoesNotExist
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick

diff --git a/core/ui/src/androidTest/kotlin/org/meshtastic/core/ui/util/AlertManagerUiTest.kt b/core/ui/src/androidTest/kotlin/org/meshtastic/core/ui/util/AlertManagerUiTest.kt
index d2a13ff38b..5632d39c1a 100644
--- a/core/ui/src/androidTest/kotlin/org/meshtastic/core/ui/util/AlertManagerUiTest.kt
+++ b/core/ui/src/androidTest/kotlin/org/meshtastic/core/ui/util/AlertManagerUiTest.kt
@@ -19,7 +19,7 @@ package org.meshtastic.core.ui.util
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import org.junit.Rule

diff --git a/feature/firmware/build.gradle.kts b/feature/firmware/build.gradle.kts
index 8fee603bfc..c654e6e6fc 100644
--- a/feature/firmware/build.gradle.kts
+++ b/feature/firmware/build.gradle.kts
@@ -58,11 +58,12 @@ kotlin {
androidMain.dependencies { implementation(libs.markdown.renderer.android) }
+ commonTest.dependencies { implementation(projects.core.testing) }
+
val androidHostTest by getting {
dependencies {
implementation(libs.junit)
implementation(libs.kotlinx.coroutines.test)
- implementation(libs.androidx.compose.ui.test.junit4)
implementation(libs.androidx.test.ext.junit)
}
}

diff --git a/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/ota/dfu/DfuZipParser.kt b/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/ota/dfu/DfuZipParser.kt
index 2763aa414c..10a0a51545 100644
--- a/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/ota/dfu/DfuZipParser.kt
+++ b/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/ota/dfu/DfuZipParser.kt
@@ -17,7 +17,9 @@
package org.meshtastic.feature.firmware.ota.dfu
import co.touchlab.kermit.Logger
+import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.JsonDecodingException
private val json = Json { ignoreUnknownKeys = true }
@@ -36,7 +38,11 @@ internal fun parseDfuZipEntries(entries: Map<String, ByteArray>): DfuZipPackage
val manifest =
runCatching { json.decodeFromString<DfuManifest>(manifestBytes.decodeToString()) }
- .getOrElse { e -> throw DfuException.InvalidPackage("Failed to parse manifest.json: ${e.message}") }
+ .getOrElse { e ->
+ @OptIn(ExperimentalSerializationApi::class)
+ val detail = (e as? JsonDecodingException)?.shortMessage ?: e.message
+ throw DfuException.InvalidPackage("Failed to parse manifest.json: $detail")
+ }
val entry =
manifest.manifest.primaryEntry ?: throw DfuException.InvalidPackage("No firmware entry found in manifest.json")

diff --git a/feature/intro/build.gradle.kts b/feature/intro/build.gradle.kts
index fe05a2b43a..242c75bccc 100644
--- a/feature/intro/build.gradle.kts
+++ b/feature/intro/build.gradle.kts
@@ -44,7 +44,6 @@ kotlin {
implementation(libs.junit)
implementation(project.dependencies.platform(libs.androidx.compose.bom))
implementation(libs.kotlinx.coroutines.test)
- implementation(libs.androidx.compose.ui.test.junit4)
}
}
}

diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index c5f0f5cad5..d216919506 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -68,7 +68,6 @@ osmdroid-android = "6.1.20"
spotless = "8.4.0"
wire = "6.2.0"
vico = "3.1.0"
-dependency-guard = "0.5.0"
kable = "0.42.0"
kmqtt = "1.0.0"
jmdns = "3.6.3"
@@ -141,7 +140,6 @@ compose-multiplatform-ui-tooling-preview = { module = "org.jetbrains.compose.ui:
compose-multiplatform-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "compose-multiplatform" }
compose-multiplatform-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "compose-multiplatform-material3" }
-
# JetBrains Material 3 Adaptive (multiplatform — Android, Desktop, iOS)
jetbrains-compose-material3-adaptive = { module = "org.jetbrains.compose.material3.adaptive:adaptive", version.ref = "jetbrains-adaptive" }
jetbrains-compose-material3-adaptive-layout = { module = "org.jetbrains.compose.material3.adaptive:adaptive-layout", version.ref = "jetbrains-adaptive" }
@@ -174,7 +172,6 @@ qrcode-kotlin = { module = "io.github.g0dkar:qrcode-kotlin", version.ref = "qrco
kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
dokka-gradlePlugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" }
-
kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.4.0" }
kotlinx-atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version = "0.32.1" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines-android" }
@@ -199,13 +196,10 @@ androidx-test-ext-junit = { module = "androidx.test.ext:junit", version = "1.3.0
androidx-test-runner = { module = "androidx.test:runner", version = "1.7.0" }
androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version = "3.7.0" }
junit = { module = "junit:junit", version = "4.13.2" }
-junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit5" }
junit-vintage-engine = { module = "org.junit.vintage:junit-vintage-engine", version.ref = "junit5" }
junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junit-platform" }
-mokkery-library = { module = "dev.mokkery:mokkery-runtime", version.ref = "mokkery" }
kotest-assertions = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" }
kotest-property = { module = "io.kotest:kotest-property", version.ref = "kotest" }
-kotest-framework = { module = "io.kotest:kotest-framework-engine", version.ref = "kotest" }
kotest-runner-junit6 = { module = "io.kotest:kotest-runner-junit6", version.ref = "kotest" }
robolectric = { module = "org.robolectric:robolectric", version = "4.16.1" }
turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }

Served by rngit 1.5.2 - Generated in 0.26s